﻿#region Copyright ?2010 Pawel Idzikowski [idzikowski@sharpserializer.com]

//  ***********************************************************************
//  Project: sharpSerializer
//  Web: http://www.sharpserializer.com
//
//  This software is provided 'as-is', without any express or implied warranty.
//  In no event will the author(s) be held liable for any damages arising from
//  the use of this software.
//
//  Permission is granted to anyone to use this software for any purpose,
//  including commercial applications, and to alter it and redistribute it
//  freely, subject to the following restrictions:
//
//      1. The origin of this software must not be misrepresented; you must not
//        claim that you wrote the original software. If you use this software
//        in a product, an acknowledgment in the product documentation would be
//        appreciated but is not required.
//
//      2. Altered source versions must be plainly marked as such, and must not
//        be misrepresented as being the original software.
//
//      3. This notice may not be removed or altered from any source distribution.
//
//  ***********************************************************************

#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;

namespace CuteAnt.Serialization.Core.Binary
{
	/// <summary>Some methods which are used by IBinaryReader</summary>
	public static class BinaryReaderTools
	{
		///<summary>
		///</summary>
		///<param name = "reader"></param>
		///<returns></returns>
		public static String ReadString(BinaryReader reader)
		{
			if (!reader.ReadBoolean()) { return null; }
			return reader.ReadString();
		}

		///<summary>
		///</summary>
		///<param name = "reader"></param>
		///<returns></returns>
		public static Int32 ReadNumber(BinaryReader reader)
		{
			// Size
			Byte size = reader.ReadByte();

			// Number
			switch (size)
			{
				case NumberSize.Zero:
					return 0;

				case NumberSize.B1:
					return reader.ReadByte();

				case NumberSize.B2:
					return reader.ReadInt16();

				default:
					return reader.ReadInt32();
			}
		}

		/// <summary>
		/// </summary>
		/// <returns>empty array if there are no indexes</returns>
		public static Int32[] ReadNumbers(BinaryReader reader)
		{
			// Count
			Int32 count = ReadNumber(reader);
			if (count == 0) return new Int32[0];

			// Items
			var result = new List<Int32>();

			for (Int32 i = 0; i < count; i++)
			{
				result.Add(ReadNumber(reader));
			}
			return result.ToArray();
		}

		///<summary>
		///</summary>
		///<param name = "expectedType"></param>
		///<param name = "reader"></param>
		///<returns></returns>
		public static object ReadValue(Type expectedType, BinaryReader reader)
		{
			if (!reader.ReadBoolean()) { return null; }
			return readValueCore(expectedType, reader);
		}

		private static object readValueCore(Type type, BinaryReader reader)
		{
			try
			{
				if (type == typeof(Byte[])) { return readArrayOfByte(reader); }
				if (type == typeof(String)) { return reader.ReadString(); }
				if (type == typeof(Boolean)) { return reader.ReadBoolean(); }
				if (type == typeof(Byte)) { return reader.ReadByte(); }
				if (type == typeof(Char)) { return reader.ReadChar(); }
				if (type == typeof(DateTime)) { return new DateTime(reader.ReadInt64()); }
				if (type == typeof(Guid)) { return new Guid(reader.ReadBytes(16)); }
#if DEBUG || PORTABLE || SILVERLIGHT
				if (type == typeof(Decimal)) { return readDecimal(reader); }
#else
				if (type == typeof(Decimal)) { return reader.ReadDecimal(); }
#endif
				if (type == typeof(Double)) { return reader.ReadDouble(); }
				if (type == typeof(Int16)) { return reader.ReadInt16(); }
				if (type == typeof(Int32)) { return reader.ReadInt32(); }
				if (type == typeof(Int64)) { return reader.ReadInt64(); }
				if (type == typeof(SByte)) { return reader.ReadSByte(); }
				if (type == typeof(Single)) { return reader.ReadSingle(); }
				if (type == typeof(UInt16)) { return reader.ReadUInt16(); }
				if (type == typeof(UInt32)) { return reader.ReadUInt32(); }
				if (type == typeof(UInt64)) { return reader.ReadUInt64(); }
				if (type == typeof(TimeSpan)) { return new TimeSpan(reader.ReadInt64()); }
				if (type == typeof(Color))
				{
					var text = reader.ReadString();
					var color = text.Substring(text.IndexOf('[') + 1).Replace("]", "");
					var argb = color.Split(',', '=');
					if (argb.Length > 1)
					{
						return Color.FromArgb(Int32.Parse(argb[1]), Int32.Parse(argb[3]), Int32.Parse(argb[5]), Int32.Parse(argb[7]));
					}
					else
					{
						if (color.Equals("Empty", StringComparison.InvariantCultureIgnoreCase))
						{
							return Color.Empty;
						}
						else
						{
							return Color.FromName(color);
						}
					}
				}
				if (type == typeof(Font))
				{
					var text = reader.ReadString();
					var conv = TypeDescriptor.GetConverter(type);
					return conv.ConvertFromInvariantString(text);
				}

				// Enumeration
				if (type.IsEnum) { return readEnumeration(type, reader); }

				// Type
				if (isType(type))
				{
					var typeName = reader.ReadString();
					return Type.GetType(typeName, true);
				}
				throw new InvalidOperationException(String.Format("Unknown simple type: {0}", type.FullName));
			}
			catch (Exception ex)
			{
				throw new SimpleValueParsingException(
						String.Format("Invalid type: {0}. See details in the inner exception.", type), ex);
			}
		}

		private static object readDecimal(BinaryReader reader)
		{
			var bits = new Int32[4];
			bits[0] = reader.ReadInt32();
			bits[1] = reader.ReadInt32();
			bits[2] = reader.ReadInt32();
			bits[3] = reader.ReadInt32();
			return new Decimal(bits);
		}

		private static Boolean isType(Type type)
		{
			return type == typeof(Type) || type.IsSubclassOf(typeof(Type));
		}

		private static object readEnumeration(Type expectedType, BinaryReader reader)
		{
			// read the enum as Int32
			Int32 value = reader.ReadInt32();
			object result = Enum.ToObject(expectedType, value);
			return result;
		}

		private static Byte[] readArrayOfByte(BinaryReader reader)
		{
			Int32 length = ReadNumber(reader);
			if (length == 0) { return null; }
			return reader.ReadBytes(length);
		}
	}
}